// ***************************************************************** -*- C++ -*-
/*
 * Copyright (C) 2009 Yannick Willener <yannick@willener.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
 *
 * This software use the exiv2 library from http://www.exiv2.org/
 */
/*
  File:      exif2clipboard.cpp
  Version:   $Rev: 0.0.1$
  Author(s): Yannick Willener (yw) <yannick@willener.com>
  History:   04-Mar-09, yw: created
 */

#include <exiv2/image.hpp>
#include <exiv2/exif.hpp>

#include <windows.h>

#include <iostream>
#include <iomanip>
#include <cassert>

#define ARG_PRETAG		"-pre"
#define ARG_POSTTAG		"-post"
#define ARG_SEPARATOR	"-sep"
#define ARG_PATTERN		"-pat"

#define MAX_OUTPUT_SIZE		1024
#define MAX_PARAMETERS_SIZE	256

int main(int argc, char *argv[])
{
	char  preTag[MAX_PARAMETERS_SIZE] = {'\0'}; 
	char  postTag[MAX_PARAMETERS_SIZE] = {'\0'};
	char  separator[MAX_PARAMETERS_SIZE] = {'\0'};
	char  pattern[MAX_PARAMETERS_SIZE] = {'\0'};
	char  fileName[MAX_PARAMETERS_SIZE] = {'\0'};
	char  exifText[MAX_PARAMETERS_SIZE] = {'\0'};

	// Defaults
	sprintf(separator, "|");
	sprintf(pattern, "MIELF");

	if (argc < 2) {
		std::cout << "This application will extract the EXIF information of a\n";
		std::cout << "JPEG image and copy it to the system clipboard.\n";
		std::cout << "\n";
		std::cout << "Usage: " << argv[0] << " [optional parameters] file\n";
		std::cout << "\n";
		std::cout << "Parameters:\n";
		std::cout << "-pre      Pre-tags\n";
		std::cout << "          Default: empty\n";
		std::cout << "-post     Post-tags\n";
		std::cout << "          Default: empty\n";
		std::cout << "-sep      Separator text\n";
		std::cout << "          Default: |\n";
		std::cout << "-pat      Pattern of exif data\n";
		std::cout << "          Can be any combination of the following:\n";
		std::cout << "          M : Camera model\n";
		std::cout << "          I : ISO\n";
		std::cout << "          E : Exposure time\n";
		std::cout << "          L : Focal length\n";
		std::cout << "          F : Focal number\n";
		std::cout << "          Default: MIELF\n";
		return 1;
	}

	for (long i = 1; i < argc; i++)
	{
		if		((strcmp(ARG_PRETAG, argv[i]) == 0) && (i+1 <= argc-1))
			strncpy(preTag, argv[i+1], MAX_PARAMETERS_SIZE-1); 
		else if	((strcmp(ARG_POSTTAG, argv[i]) == 0) && (i+1 <= argc-1))
			strncpy(postTag, argv[i+1], MAX_PARAMETERS_SIZE-1);
		else if	((strcmp(ARG_SEPARATOR, argv[i]) == 0) && (i+1 <= argc-1))
			strncpy(separator, argv[i+1], MAX_PARAMETERS_SIZE-1);
		else if	((strcmp(ARG_PATTERN, argv[i]) == 0) && (i+1 <= argc-1))
			strncpy(pattern, argv[i+1], MAX_PARAMETERS_SIZE-1);
	}
	strncpy(fileName, argv[argc-1], MAX_PARAMETERS_SIZE-1);

	try {
		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(fileName);
		assert(image.get() != 0);
		image->readMetadata();

		Exiv2::ExifData &exifData = image->exifData();
		if (exifData.empty()) {
			std::string error(argv[1]);
			error += ": No Exif data found in the file";
			throw Exiv2::Error(1, error);
		}

		// Include the prefix if needed
		if (strlen(preTag)) 
			strncat(exifText, preTag, MAX_OUTPUT_SIZE - strlen(exifText) - 1);

		// Include the specified fields
		for (unsigned long i = 0; i < strlen(pattern); i++)
		{
			std::ostringstream oss;
			switch (pattern[i])
			{
			case 'M': case 'm': oss << exifData["Exif.Image.Model"]; break;
			case 'I': case 'i': oss << "ISO " << exifData["Exif.Photo.ISOSpeedRatings"]; break;
			case 'E': case 'e': oss << exifData["Exif.Photo.ExposureTime"]; break;
			case 'L': case 'l': oss << exifData["Exif.Photo.FocalLength"]; break;
			case 'F': case 'f': oss << exifData["Exif.Photo.FNumber"]; break;
			default : throw Exiv2::Error(1, "Invalin character in pattern.");
			}
			strncat(exifText, oss.str().c_str(), MAX_OUTPUT_SIZE - strlen(exifText) - 1);

			if (i < strlen(pattern) - 1)
			{
				strncat(exifText, " ", MAX_OUTPUT_SIZE - strlen(exifText) - 1);
				strncat(exifText, separator, MAX_OUTPUT_SIZE - strlen(exifText) - 1);
				strncat(exifText, " ", MAX_OUTPUT_SIZE - strlen(exifText) - 1);
			}
		} 
		
		// Include the postfix if needed
		if (strlen(postTag)) 
			strncat(exifText, postTag, MAX_OUTPUT_SIZE - strlen(exifText) - 1);

		// Copy to clipboard
		if (OpenClipboard(NULL))
		{	
			EmptyClipboard();
			HGLOBAL hClipboardData;
			hClipboardData = GlobalAlloc(GMEM_DDESHARE, sizeof(exifText) + 1);
			char * pchData;
			pchData = (char *)GlobalLock(hClipboardData);
			strcpy(pchData, LPCSTR(exifText));
			GlobalUnlock(hClipboardData);
			SetClipboardData(CF_TEXT, hClipboardData);
			CloseClipboard();
		}

		return 0;
	}
	catch (Exiv2::AnyError& e) {
		std::cout << "Error '" << e << "'\n";
		return -1;
	}
}
